;; Copyright (c) [2022] [bobwxc]
;; This file is licensed under Mulan PubL v2.
;; You can use this software according to the terms and conditions of the Mulan PubL v2.
;; You may obtain a copy of Mulan PubL v2 at:
;;          http://license.coscl.org.cn/MulanPubL-2.0
;; THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
;; EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
;; MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
;; See the Mulan PubL v2 for more details.

(in-package #:cl-mdb)

(defun init-root-list ()
  (list (init-index-array)
        (init-store-list))) ;; return list (#(index-array) (store-list))

;; (print (init-root-list))

(defun insert (root-list key value)
  (declare (string key)
           ((or number character string sequence) value))
  (let ((r (select-index (car root-list) key)))
    (when (and (not (equal r nil)) ;; already existed
               (not (row-remove-flag (cadr r)))) ;; remove-flag is nil
      (return-from insert nil)))
  ;; insert value
  (setf (cadr root-list) (insert-value (cadr root-list) key value))
  ;; insert index
  (setf (car root-list) (insert-index (car root-list) key (car (cadr root-list))))
  ;; active r/w flag
  (setf (row-read-flag (car (cadr root-list))) t)
  (setf (row-write-flag (car (cadr root-list))) t)
  root-list)

;; (print (insert (init-root-list) "123" "abc"))

(defun update (root-list key new-value)
  (let ((r (select-index (car root-list) key)))
    (if r
        (let ((prow (cadr r)))
          ;; remove?
          (when (row-remove-flag prow)
            (return-from update nil))
          ;; r/w flag wait unlock
          (do ((r (row-read-flag prow))
               (w (row-write-flag prow)))
              ((and r w))
              (sleep 0.01)) ;; 10ms
          ;; lock r/w flag
          (setf (row-read-flag prow) nil)
          (setf (row-write-flag prow) nil)
          ;; update value / time / id
          (setf (row-content prow) new-value)
          (setf (row-time prow) (get-universal-time))
          (setf (row-operation-id prow) (snid:genid *node-id*))
          ;; reverse flag
          (setf (row-read-flag prow) t)
          (setf (row-write-flag prow) t)
          root-list)
        nil)))

;; (print (update (insert (init-root-list) "123" "abc") "123" "efg"))

(defun select (root-list key)
  (let ((r (select-index (car root-list) key)))
    (if r
        (let ((prow (cadr r)))
          (if (and (not (row-remove-flag prow))
                   (row-read-flag prow))
              (row-content prow)
            nil))
        nil)))

;; (print (select (insert (init-root-list) "123" "abc") "123"))

(defun remov (root-list key)
  (let ((r (select-index (car root-list) key)))
    (if r
        (let ((prow (cadr r)))
        ;; check remove flag
        (when (row-remove-flag prow)
          (return-from remov nil))
        ;; r/w flag wait
        (do ((r (row-read-flag prow))
             (w (row-write-flag prow)))
            ((and r w))
            (sleep 0.01)) ;; 10ms
        ;; lock r/w flag
        (setf (row-read-flag prow) nil)
        (setf (row-write-flag prow) nil)
        ;; set remove t (waiting for gc)
        (setf (row-remove-flag prow) t)
        ;; update id
        (setf (row-operation-id prow) (snid:genid *node-id*))
        root-list)
      nil)))

;; (print (remov (insert (init-root-list) "123" "abc") "123"))

(defun gbc (root-list)
  (let ((gc-key-list '())
        )
    (setf (cadr root-list)
          (mapcar #'(lambda (prow) (if (row-remove-flag prow)
                                       (progn
                                         (push (row-original-key prow) gc-key-list)
                                         nil)
                                     prow))
                  (cadr root-list)))
    (setf (car root-list) (gc-index (car root-list) gc-key-list))
    (setf (cadr root-list) (remove nil (cadr root-list)))
    root-list))

;; (gbc (remov (insert (init-root-list) "123" "abc") "123"))

(defun mdump (root-list path)
  (with-open-file (fp path
                      :direction :output
                      :element-type 'character
                      :if-exists :supersede
                      :if-does-not-exist :create)
                  (format fp "~S" (cadr root-list))))

;; (mdump (insert (init-root-list) "123" "abc") "./test.txt")

(defun mload (path)
  (let* ((store-list (with-open-file (fp path
                                        :direction :input
                                        :element-type 'character
                                        :if-does-not-exist :error)
                                    (read fp)))
         (root-list (list (init-index-array) store-list))
        )
    (re-index root-list)
    )
  )

;; (mdump (insert (init-root-list) "123" "abc") "./test.txt")
;; (mload "./test.txt")

